package bancosys.tec.utils.date;

import java.util.HashMap;
import java.util.Map;

import jmine.tec.utils.date.WeekDaysEnum;

/**
 * Representa um dia juliano.
 * 
 * @author Marco
 */
public class JulianDay implements Comparable<JulianDay> {

    /**
     * Mapa onde a chave é o dia da semana, e o valor é o próximo dia da semana.
     */
    private static final Map<WeekDaysEnum, WeekDaysEnum> NEXT_WEEKDAY = new HashMap<WeekDaysEnum, WeekDaysEnum>();

    private static final Map<WeekDaysEnum, WeekDaysEnum> LAST_WEEKDAY = new HashMap<WeekDaysEnum, WeekDaysEnum>();

    /**
     * Inicializa o mapa com os dias da semana.
     */
    static {
        NEXT_WEEKDAY.put(WeekDaysEnum.SUNDAY, WeekDaysEnum.MONDAY);
        NEXT_WEEKDAY.put(WeekDaysEnum.MONDAY, WeekDaysEnum.TUESDAY);
        NEXT_WEEKDAY.put(WeekDaysEnum.TUESDAY, WeekDaysEnum.WEDNESDAY);
        NEXT_WEEKDAY.put(WeekDaysEnum.WEDNESDAY, WeekDaysEnum.THURSDAY);
        NEXT_WEEKDAY.put(WeekDaysEnum.THURSDAY, WeekDaysEnum.FRIDAY);
        NEXT_WEEKDAY.put(WeekDaysEnum.FRIDAY, WeekDaysEnum.SATURDAY);
        NEXT_WEEKDAY.put(WeekDaysEnum.SATURDAY, WeekDaysEnum.SUNDAY);

        LAST_WEEKDAY.put(WeekDaysEnum.SUNDAY, WeekDaysEnum.SATURDAY);
        LAST_WEEKDAY.put(WeekDaysEnum.MONDAY, WeekDaysEnum.SUNDAY);
        LAST_WEEKDAY.put(WeekDaysEnum.TUESDAY, WeekDaysEnum.MONDAY);
        LAST_WEEKDAY.put(WeekDaysEnum.WEDNESDAY, WeekDaysEnum.TUESDAY);
        LAST_WEEKDAY.put(WeekDaysEnum.THURSDAY, WeekDaysEnum.WEDNESDAY);
        LAST_WEEKDAY.put(WeekDaysEnum.FRIDAY, WeekDaysEnum.THURSDAY);
        LAST_WEEKDAY.put(WeekDaysEnum.SATURDAY, WeekDaysEnum.FRIDAY);
    }

    /**
     * Dia juliano.
     */
    private int day;

    /**
     * Dia da semana.
     */
    private WeekDaysEnum weekday;

    /**
     * Construtor.
     * 
     * @param day dia juliano.
     * @param weekday dia da semana.
     */
    public JulianDay(int day, WeekDaysEnum weekday) {
        this.day = day;
        this.weekday = weekday;
    }

    /**
     * Retorna dia juliano.
     * 
     * @return dia juliano.
     */
    public int getDay() {
        return this.day;
    }

    /**
     * Retorna o dia da semana.
     * 
     * @return dia da semana.
     */
    public WeekDaysEnum getWeekday() {
        return this.weekday;
    }

    /**
     * Constrói um dia juliano representando o próximo dia.
     * 
     * @return objeto com o próximo dia.
     */
    public JulianDay next() {
        WeekDaysEnum next = NEXT_WEEKDAY.get(this.weekday);
        return new JulianDay(this.day + 1, next);
    }

    /**
     * Constrói um dia juliano representando o dia anterior.
     * 
     * @return objeto com o dia anterior.
     */
    public JulianDay last() {
        WeekDaysEnum last = LAST_WEEKDAY.get(this.weekday);
        return new JulianDay(this.day - 1, last);
    }

    /**
     * Se essa data é anterior à data informada.
     * 
     * @param other data a ser comparada.
     * @return <code>true</code> caso essa data anterior à data informada, ou <code>false</code> caso contrário.
     */
    public boolean before(JulianDay other) {
        return this.compareTo(other) == -1;
    }

    /**
     * Se essa data é posterior à data informada.
     * 
     * @param other data a ser comparada.
     * @return <code>true</code> caso essa data seja posterior à data informada, ou <code>false</code> caso contrário.
     */
    public boolean after(JulianDay other) {
        return this.compareTo(other) == 1;
    }

    /**
     * Compara outro objeto considerando apenas o dia.
     * <p>
     * {@inheritDoc}
     * 
     * @see JulianDay#compareTo(JulianDay)
     */
    public int compareTo(JulianDay other) {
        return this.day == other.day ? 0 : (this.day > other.day ? 1 : -1);
    }

    /**
     * Calculado apenas sobre o dia.
     * <p>
     * {@inheritDoc}
     * 
     * @see JulianDay#hashCode()
     */
    @Override
    public int hashCode() {
        final int prime = 31;
        return prime + this.day;
    }

    /**
     * {@inheritDoc}
     * 
     * @return <code>true</code> caso seja do tipo JulianDay e o dia seja o mesmo. Ignora o dia da semana.
     * @see JulianDay#equals(Object)
     */
    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof JulianDay)) {
            return false;
        }
        JulianDay other = (JulianDay) obj;
        return this.day == other.day;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return "JulianDay(" + this.day + "; " + this.weekday + ")";
    }
}
